/****************************************************************
 * Fulguro
 * Copyright (C) 2004 Christophe Clienti
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 ***************************************************************/

#include "flgrCoreMalloc.h"
#include "flgrCoreThreads.h"
#include "flgrCoreThreadsAuto.h"



  /*!
   * \defgroup group_fulguro_core_thread_auto Threads auto
   * \ingroup group_fulguro_core
   * @{
   */


int __internal_fulguro_thread_data2d_data2d(void *param) {
  FLGR_ThreadsArg *arg = (FLGR_ThreadsArg *) param;
  FLGR_Fct_data2d_data2d *fct = (FLGR_Fct_data2d_data2d *) arg->argv[0]; 

  FLGR_Data2D *p1 = (FLGR_Data2D *) arg->argv[1];
  FLGR_Data2D *p2 = (FLGR_Data2D *) arg->argv[2];
 
  return (int) ((*fct)(p1,p2));

}


int __internal_fulguro_thread_data2d_data2d_nhb(void *param) {
  FLGR_ThreadsArg *arg = (FLGR_ThreadsArg *) param;
  FLGR_Fct_data2d_data2d_data2d *fct = (FLGR_Fct_data2d_data2d_data2d *) arg->argv[0]; 

  FLGR_Data2D *p1 = (FLGR_Data2D *) arg->argv[1];
  FLGR_Data2D *p2 = (FLGR_Data2D *) arg->argv[2];
  FLGR_Data2D *p3 = (FLGR_Data2D *) arg->argv[3];

  return (int) ((*fct)(p1,p2,p3));

}


int __internal_fulguro_thread_data2d_data2d_data2d(void *param) {
  FLGR_ThreadsArg *arg = (FLGR_ThreadsArg *) param;
  FLGR_Fct_data2d_data2d_data2d *fct = (FLGR_Fct_data2d_data2d_data2d *) arg->argv[0]; 

  FLGR_Data2D *p1 = (FLGR_Data2D *) arg->argv[1];
  FLGR_Data2D *p2 = (FLGR_Data2D *) arg->argv[2];
  FLGR_Data2D *p3 = (FLGR_Data2D *) arg->argv[3];

  return (int) ((*fct)(p1,p2,p3));

}


////////////////////////////////////////////////////////////////////////////
/*! Use Multiple threads to execute a function using two data2d as parameters
 *  @param fct_ptr : pointer to a function to execute using multiple thread 
 *                   as \code FLGR_Ret fct(FLGR_Data2D *dest, FLGR_Data2D *src); \endcode
 *  @param p1 : first parameter of the function to execute (ie "dest")
 *  @param p2 : second parameter of the function to execute (ie "src")
 *  @param thread_number : number of thread to launch
 *  @returns FLGR_RET_OK
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_thread_function_data2d_data2d(FLGR_Fct_data2d_data2d *fct_ptr, FLGR_Data2D *p1, FLGR_Data2D *p2, int thread_number) {
  
  FLGR_Data2D **p1_link = (FLGR_Data2D **) flgr_malloc(sizeof(FLGR_Data2D*) * thread_number);
  FLGR_Data2D **p2_link = (FLGR_Data2D **) flgr_malloc(sizeof(FLGR_Data2D*) * thread_number);
  FLGR_ThreadsArgList *arglist = flgr_threads_create_argument_list(thread_number);
  int i;
  
  if( (fct_ptr == NULL) || (p1==NULL) || (p2==NULL) ) {
    POST_ERROR("Null objects\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(thread_number<=0) {
    thread_number=1;
  }

  for(i=0 ; i<thread_number ; i++) {
    p1_link[i] = flgr2d_create_pixmap_link(p1, thread_number, i, 0);
    p2_link[i] = flgr2d_create_pixmap_link(p2, thread_number, i, 0);

    flgr_threads_set_argument(arglist, i, 3, fct_ptr, p1_link[i], p2_link[i]);
  }

  flgr_threads_start(arglist, thread_number, 1, __internal_fulguro_thread_data2d_data2d);

  flgr_threads_destroy_argument_list(arglist);

  for(i=0 ; i<thread_number ; i++) {
    flgr2d_destroy_link(p1_link[i]);
    flgr2d_destroy_link(p2_link[i]);
  }

  flgr_free(p1_link);
  flgr_free(p2_link);

  return FLGR_RET_OK;
}



////////////////////////////////////////////////////////////////////////////
/*! Use Multiple threads to execute a neighborhood function using three data2d
 *  @param fct_ptr : pointer to a function to execute using multiple thread
 *                   as \code FLGR_Ret fct(FLGR_Data2D *dest, FLGR_Data2D *src, FLGR_Data2D *nhb); \endcode
 *  @param p1 : first parameter of the function to execute (ie "dest")
 *  @param p2 : second parameter of the function to execute (ie "src")
 *  @param nhb : neighborhood parameter
 *  @param thread_number : number of thread to launch
 *  @returns FLGR_RET_OK
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_thread_function_data2d_data2d_nhb(FLGR_Fct_data2d_data2d_data2d *fct_ptr, FLGR_Data2D *p1, FLGR_Data2D *p2, FLGR_Data2D *nhb,
						  int thread_number) {
  
  FLGR_Data2D **p1_link = (FLGR_Data2D **) flgr_malloc(sizeof(FLGR_Data2D*) * thread_number);
  FLGR_Data2D **p2_link = (FLGR_Data2D **) flgr_malloc(sizeof(FLGR_Data2D*) * thread_number);
  FLGR_ThreadsArgList *arglist = flgr_threads_create_argument_list(thread_number);
  int overlap = nhb->size_y/2;
  int i;
  
  if( (fct_ptr == NULL) || (p1==NULL) || (p2==NULL) || (nhb==NULL) ) {
    POST_ERROR("Null objects\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(thread_number<=0) {
    thread_number=1;
  }

  for(i=0 ; i<thread_number ; i++) {
    p1_link[i] = flgr2d_create_pixmap_link(p1, thread_number, i, overlap);
    p2_link[i] = flgr2d_create_pixmap_link(p2, thread_number, i, overlap);

    flgr_threads_set_argument(arglist, i, 4, fct_ptr, p1_link[i], p2_link[i], nhb);
  }

  flgr_threads_start(arglist, thread_number, 1, __internal_fulguro_thread_data2d_data2d_nhb);

  flgr_threads_destroy_argument_list(arglist);

  for(i=0 ; i<thread_number ; i++) {
    flgr2d_destroy_link(p1_link[i]);
    flgr2d_destroy_link(p2_link[i]);
  }

  flgr_free(p1_link);
  flgr_free(p2_link);

  return FLGR_RET_OK;
}



////////////////////////////////////////////////////////////////////////////
/*! Use Multiple threads to execute a neighborhood function using three data2d
 *  @param fct_ptr : pointer to a function to execute using multiple thread
 *                   as \code FLGR_Ret fct(FLGR_Data2D *dest, FLGR_Data2D *src1, FLGR_Data2D *src2); \endcode
 *  @param p1 : first parameter of the function to execute (ie "dest")
 *  @param p2 : second parameter of the function to execute (ie "src1")
 *  @param p3 : third parameter of the function to execute (ie "src2")
 *  @param thread_number : number of thread to launch
 *  @returns FLGR_RET_OK
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_thread_function_data2d_data2d_data2d(FLGR_Fct_data2d_data2d_data2d *fct_ptr, FLGR_Data2D *p1, FLGR_Data2D *p2, FLGR_Data2D *p3,
						     int thread_number) {
  
  FLGR_Data2D **p1_link = (FLGR_Data2D **) flgr_malloc(sizeof(FLGR_Data2D*) * thread_number);
  FLGR_Data2D **p2_link = (FLGR_Data2D **) flgr_malloc(sizeof(FLGR_Data2D*) * thread_number);
  FLGR_Data2D **p3_link = (FLGR_Data2D **) flgr_malloc(sizeof(FLGR_Data2D*) * thread_number);
  FLGR_ThreadsArgList *arglist = flgr_threads_create_argument_list(thread_number);
  int i;
  
  if( (fct_ptr == NULL) || (p1==NULL) || (p2==NULL) || (p3==NULL) ) {
    POST_ERROR("Null objects\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(thread_number<=0) {
    thread_number=1;
  }

  for(i=0 ; i<thread_number ; i++) {
    p1_link[i] = flgr2d_create_pixmap_link(p1, thread_number, i, 0);
    p2_link[i] = flgr2d_create_pixmap_link(p2, thread_number, i, 0);
    p3_link[i] = flgr2d_create_pixmap_link(p3, thread_number, i, 0);

    flgr_threads_set_argument(arglist, i, 4, fct_ptr, p1_link[i], p2_link[i], p3_link[i]);
  }

  flgr_threads_start(arglist, thread_number, 1, __internal_fulguro_thread_data2d_data2d_data2d);

  flgr_threads_destroy_argument_list(arglist);

  for(i=0 ; i<thread_number ; i++) {
    flgr2d_destroy_link(p1_link[i]);
    flgr2d_destroy_link(p2_link[i]);
    flgr2d_destroy_link(p3_link[i]);
  }

  flgr_free(p1_link);
  flgr_free(p2_link);
  flgr_free(p3_link);

  return FLGR_RET_OK;
}



//! @}
